<?php

namespace tmcore\utils;


class Rsa
{
    private $privateKey = '';
    private $publicKey = '';
    private $padding = OPENSSL_PKCS1_PADDING;

    //初始化
    public function __construct($priKey, $pubKey, $is_format = true)
    {
        $this->privateKey = openssl_pkey_get_private($is_format?$this->formatPriKey($priKey):$priKey);//私钥，用于加密
        $this->publicKey  = openssl_pkey_get_public($is_format?$this->formatPubKey($pubKey):$pubKey);//公钥，用于解密
    }

    /**
     * 格式化私钥
     * @param string $priKey 私钥
     * @return string 返回格式化的值
     */
    public function formatPriKey($priKey)
    {
        $fKey = "-----BEGIN PRIVATE KEY-----\n";
        $len  = strlen($priKey);
        for ($i = 0; $i < $len;) {
            $fKey = $fKey . substr($priKey, $i, 64) . "\n";
            $i    += 64;
        }
        $fKey .= "-----END PRIVATE KEY-----";
        return $fKey;
    }

    /**
     * 格式化公钥
     * @param string $pubKey 公钥
     * @return string 返回格式化的值
     */
    public function formatPubKey($pubKey)
    {
        $fKey = "-----BEGIN PUBLIC KEY-----\n";
        $len  = strlen($pubKey);
        for ($i = 0; $i < $len;) {
            $fKey = $fKey . substr($pubKey, $i, 64) . "\n";
            $i    += 64;
        }
        $fKey .= "-----END PUBLIC KEY-----";
        return $fKey;
    }

    /**
     * 私钥加密
     * @param string $data 原始数据
     * @return string 密文结果
     */
    public function encryptByPrivateKey($data)
    {
        openssl_private_encrypt($data, $encrypted, $this->privateKey, $this->padding);
        $encrypted = base64_encode($encrypted);
        return $encrypted;
    }

    /**
     * 私钥解密
     * @param string $data 密文数据
     * @return string 原文数据结果
     */
    public function decryptByPrivateKey($data)
    {
        $crypto = '';
        $length = $this->getKeyLength() / 8;

        // 兼容长加密串
        foreach (str_split($this->_base64Decode($data), $length) as $chunk) {
            openssl_private_decrypt($chunk, $encrypted, $this->privateKey, $this->padding);
            $crypto .= $encrypted;
        }

        return $crypto;
    }

    /**
     * 私钥签名
     * @param string $data
     * @param int $signature_alg
     * @return string
     */
    public function signByPrivateKey($data, $signature_alg = OPENSSL_ALGO_SHA256)
    {
        openssl_sign($data, $signature, $this->privateKey, $signature_alg);
        return base64_encode($signature);
    }

    /**
     * 公钥加密
     * @param string $data 原文数据
     * @return string 加密结果
     */
    public function encryptByPublicKey($data)
    {
        $crypto = '';
        $length = $this->getKeyLength() / 8 - 11;

        // 兼容长加密串
        foreach (str_split($data, $length) as $chunk) {
            openssl_public_encrypt($chunk, $decrypted, $this->publicKey, $this->padding);
            $crypto .= $decrypted;
        }

        return base64_encode($crypto);
    }

    /**
     * 公钥解密
     * @param string $data 密文数据
     * @return string 原文结果
     */
    public function decryptByPublicKey($data)
    {
        $data = base64_decode($data);
        openssl_public_decrypt($data, $decrypted, $this->publicKey, $this->padding);
        return $decrypted;
    }

    /**
     * 公钥验签
     * @param string $data 原数据
     * @param string $sign 签名
     * @param int $signature_alg
     * @return int
     */
    public function verifyByPublicKey($data, $sign, $signature_alg = OPENSSL_ALGO_SHA256)
    {
        $sign = base64_decode($sign);
        return openssl_verify($data, $sign, $this->publicKey, $signature_alg);
    }

    /**
     * base_encode
     * @param string $value 待编码字符串
     * @return mixed
     */
    private function _base64Encode($value)
    {
        $data = base64_encode($value);
        return str_replace(['+', '/', '='], ['-', '_', ''], $data);
    }

    /**
     * base_decode
     * @param string $value 待解码字符串
     * @return bool|string
     */
    private function _base64Decode($value)
    {
        $data = str_replace(['-', '_'], ['+', '/'], $value);

        if ($mod4 = strlen($data) % 4) {
            $data .= substr('====', $mod4);
        }

        return base64_decode($data);
    }

    /**
     * 获取密钥长度
     * @return mixed
     */
    private function getKeyLength()
    {
        return openssl_pkey_get_details($this->publicKey)['bits'];
    }

    /**
     * 生成rsa的私钥和公钥
     * @param string $config_path openssl.cnf文件路径，php7自带
     * @param integer $key_bits 秘钥长度，默认为 2048，可选512 1024 2048 4096
     * @param string $method 加密方法，默认sha256，可用 openssl_get_md_methods 查看支持的加密方法
     * @return array 返回公私钥
     */
    public static function createRsaKey($config_path = "/usr/local/openssl/openssl.cnf", $key_bits = 2048, $method = "sha256") {
        $config = array(
            'config'           => $config_path,
            'digest_alg'       => $method,
            'private_key_bits' => $key_bits,
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
        );
        $res = openssl_pkey_new($config);
        openssl_pkey_export($res, $private_key_pem,null,$config);

        $details = openssl_pkey_get_details($res);
        $public_key_pem = $details['key'];

        return [
            'priKey' => $private_key_pem,
            'pubKey' => $public_key_pem
        ];
    }


    public function __destruct()
    {
        openssl_free_key($this->privateKey);
        openssl_free_key($this->publicKey);
    }
}
